package cn.silver.module.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.ObjectUtil;
import cn.silver.framework.core.constant.Constants;
import cn.silver.framework.core.constant.UserConstants;
import cn.silver.framework.core.enums.SystemStatusEnums;
import cn.silver.framework.core.exception.AuthException;
import cn.silver.framework.core.exception.ServiceException;
import cn.silver.framework.core.utils.MapstructUtils;
import cn.silver.framework.core.utils.StreamUtils;
import cn.silver.framework.core.utils.StringUtils;
import cn.silver.framework.core.utils.TreeBuildUtils;
import cn.silver.framework.mybatis.domain.TreeEntity;
import cn.silver.framework.mybatis.service.impl.TreeServiceImpl;
import cn.silver.framework.openapi.constant.ResultEnum;
import cn.silver.framework.satoken.model.LoginUser;
import cn.silver.framework.satoken.utils.LoginHelper;
import cn.silver.module.authority.bean.RoleBean;
import cn.silver.module.authority.bean.SysTenantGroupBean;
import cn.silver.module.system.bean.MenuBean;
import cn.silver.module.system.domain.SysMenu;
import cn.silver.module.authority.domain.SysRole;
import cn.silver.module.authority.domain.SysRoleMenu;
import cn.silver.module.system.mapper.SysMenuMapper;
import cn.silver.module.authority.mapper.SysRoleMenuMapper;
import cn.silver.module.system.model.MetaModel;
import cn.silver.module.system.model.RouterModel;
import cn.silver.module.system.param.MenuParam;
import cn.silver.module.system.service.ISysMenuService;
import cn.silver.module.authority.service.ISysRoleService;
import cn.silver.module.authority.service.ISysTenantGroupService;
import cn.silver.module.system.enums.MenuTypeEnums;
import cn.silver.module.system.enums.RoleTypeEnums;
import cn.silver.module.system.service.ISysUserRoleService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 菜单 业务层处理
 *
 * @author Silver Zhou
 */
@Service
@RequiredArgsConstructor
public class SysMenuServiceImpl extends TreeServiceImpl<SysMenuMapper, SysMenu, MenuParam, MenuBean> implements ISysMenuService {

    private final ISysRoleService roleService;
    private final ISysUserRoleService userRoleService;
    private final SysRoleMenuMapper roleMenuMapper;
    private final ISysTenantGroupService groupService;

    /**
     * 根据用户查询系统菜单列表
     *
     * @param userId 用户ID
     * @return 菜单列表
     */
    @Override
    public List<MenuBean> selectMenuList(String userId) {
        return selectMenuList(new MenuBean(), userId);
    }

    /**
     * 查询系统菜单列表
     *
     * @param menu 菜单信息
     * @return 菜单列表
     */
    @Override
    public List<MenuBean> selectMenuList(MenuBean menu, String userId) {
        List<RoleBean> roles = this.userRoleService.selectRoleGroup(userId);
        List<String> permissions = roles.stream().map(RoleBean::getType).toList();
        List<MenuBean> menuList;
        // 管理员显示所有菜单信息
        if (permissions.contains(RoleTypeEnums.SUPER_ROLE.getCode())) {
            menuList = baseMapper.selectVoList(new LambdaQueryWrapper<SysMenu>()
                    .like(StringUtils.isNotBlank(menu.getName()), SysMenu::getName, menu.getName())
                    .eq(menu.getVisible() != null, SysMenu::getVisible, menu.getVisible())
                    .eq(StringUtils.isNotBlank(menu.getStatus()), SysMenu::getStatus, menu.getStatus())
                    .orderByAsc(SysMenu::getPid)
                    .orderByAsc(SysMenu::getSort));
        } else {
            QueryWrapper<SysMenu> wrapper = Wrappers.query();
            List<String> roleIds = roles.stream().map(RoleBean::getId).toList();
            wrapper.like(StringUtils.isNotBlank(menu.getName()), "name", menu.getName())
                    .eq(menu.getVisible() != null, "visible", menu.getVisible())
                    .eq(StringUtils.isNotBlank(menu.getStatus()), "status", menu.getStatus())
                    .exists("select 1 from sys_role_menu rm where rm.menu_id = sys_menu.id and rm.role_id in {0}", roleIds)
                    .orderByAsc(Arrays.asList("pid", "sort"));
            List<SysMenu> list = baseMapper.selectList(wrapper);
            menuList = MapstructUtils.convert(list, MenuBean.class);
        }
        return menuList;
    }

    /**
     * 根据用户ID查询权限
     *
     * @param userId 用户ID
     * @return 权限列表
     */
    @Override
    public Set<String> selectMenuPermsByUserId(String userId) {
        Set<String> permsSet = new HashSet<>();
        List<RoleBean> roles = this.userRoleService.selectRoleGroup(userId);
        List<String> permissions = roles.stream().map(RoleBean::getType).toList();
        if (permissions.contains(RoleTypeEnums.SUPER_ROLE.getCode())) {
            permsSet.add(Constants.ALL_PERMISSION);
        } else {
            List<String> perms = baseMapper.selectMenuPermsByRoleId(userId);
            for (String perm : perms) {
                if (StringUtils.isNotEmpty(perm)) {
                    permsSet.addAll(StringUtils.splitList(perm.trim()));
                }
            }
        }
        return permsSet;
    }

    /**
     * 根据角色ID查询权限
     *
     * @param roleId 角色ID
     * @return 权限列表
     */
    @Override
    public Set<String> selectMenuPermsByRoleId(String roleId) {
        List<String> perms = baseMapper.selectMenuPermsByRoleId(roleId);
        Set<String> permsSet = new HashSet<>();
        for (String perm : perms) {
            if (StringUtils.isNotEmpty(perm)) {
                permsSet.addAll(StringUtils.splitList(perm.trim()));
            }
        }
        return permsSet;
    }

    /**
     * 根据用户ID查询菜单
     *
     * @return 菜单列表
     */
    @Override
    public List<RouterModel> selectLocalRouter() {
        LoginUser loginUser = LoginHelper.getLoginUser();
        if (loginUser == null) {
            throw new AuthException("登录信息不存在，请重新登录", ResultEnum.UNAUTHORIZED.getCode());
        }
        List<MenuBean> menus;
        LambdaQueryWrapper<SysMenu> wrapper = Wrappers.lambdaQuery();
        wrapper.in(SysMenu::getType, MenuTypeEnums.MENU_GROUP.getCode(), MenuTypeEnums.MENU_ITEM.getCode())
                .eq(SysMenu::getStatus, SystemStatusEnums.NORMAL.getCode()).orderByAsc(SysMenu::getPid).orderByAsc(SysMenu::getSort);
        if (!LoginHelper.isSuperAdmin()) {
            String roleId = loginUser.getRole() != null ? loginUser.getRole().getId() : null;
            if (StringUtils.isBlank(roleId)) {
                RoleBean bean = this.roleService.selectOne(new LambdaQueryWrapper<SysRole>().eq(SysRole::getType, RoleTypeEnums.COMMON_ROLE.getCode()));
                roleId = bean != null ? bean.getId() : null;
            }
            wrapper.exists("select 1 from sys_role_menu rm where rm.menu_id = sys_menu.id and rm.role_id = {0}", roleId);
        }
        menus = this.baseMapper.selectVoList(wrapper);
        Map<String, List<MenuBean>> menuMap = menus.stream().collect(Collectors.groupingBy(MenuBean::getPid));
        menus.stream().filter(item -> menuMap.containsKey(item.getId())).forEach(item -> item.setChildren(menuMap.get(item.getId())));
        return this.buildMenus(menus.stream().filter(item -> TreeEntity.ROOT_NODE.equalsIgnoreCase(item.getPid())).toList());
    }

    /**
     * 根据角色ID查询菜单树信息
     *
     * @param roleId 角色ID
     * @return 选中菜单列表
     */
    @Override
    public List<String> selectMenuListByRoleId(String roleId) {
        return baseMapper.selectMenuIdByRoleId(roleId);
    }

    /**
     * 根据租户套餐ID查询菜单树信息
     *
     * @param packageId 租户套餐ID
     * @return 选中菜单列表
     */
    @Override
    public List<String> selectMenuListByPackageId(String packageId) {
        SysTenantGroupBean group = groupService.selectById(packageId);
        List<String> menuIds = group.getMenuIds();
        if (CollUtil.isEmpty(menuIds)) {
            return List.of();
        }
        List<MenuBean> models = this.baseMapper.selectVoList(new LambdaQueryWrapper<SysMenu>().in(SysMenu::getId, menuIds));
        return CollectionUtils.isNotEmpty(models) ? models.stream().map(MenuBean::getId).toList() : List.of();
    }

    /**
     * 构建前端路由所需要的菜单
     *
     * @param menus 菜单列表
     * @return 路由列表
     */
    @Override
    public List<RouterModel> buildMenus(List<MenuBean> menus) {
        List<RouterModel> routers = new LinkedList<>();
        for (MenuBean menu : menus) {
            RouterModel router = new RouterModel();
            router.setHidden(!menu.getVisible());
            router.setName(menu.getRouteName());
            router.setPath(menu.getRouterPath());
            router.setComponent(menu.getComponentInfo());
            router.setQuery(menu.getParam());
            router.setMeta(new MetaModel(menu.getName(), menu.getIcon(), menu.getCache(), menu.getPath()));
            List<MenuBean> cMenus = menu.getChildren();
            if (CollUtil.isNotEmpty(cMenus) && MenuTypeEnums.MENU_GROUP.getCode().equals(menu.getType())) {
                router.setAlwaysShow(true);
                router.setRedirect("noRedirect");
                router.setChildren(buildMenus(cMenus));
            } else if (menu.isMenuFrame()) {
                router.setMeta(null);
                List<RouterModel> childrenList = new ArrayList<>();
                RouterModel children = new RouterModel();
                children.setPath(menu.getPath());
                children.setComponent(menu.getComponent());
                children.setName(StringUtils.capitalize(menu.getPath()));
                children.setMeta(new MetaModel(menu.getName(), menu.getIcon(), menu.getCache(), menu.getPath()));
                children.setQuery(menu.getParam());
                childrenList.add(children);
                router.setChildren(childrenList);
            } else if (menu.getPid().equalsIgnoreCase(TreeEntity.ROOT_NODE) && menu.isInnerLink()) {
                router.setMeta(new MetaModel(menu.getName(), menu.getIcon()));
                router.setPath("/");
                List<RouterModel> childrenList = new ArrayList<>();
                RouterModel children = new RouterModel();
                String routerPath = MenuBean.innerLinkReplaceEach(menu.getPath());
                children.setPath(routerPath);
                children.setComponent(UserConstants.INNER_LINK);
                children.setName(StringUtils.capitalize(routerPath));
                children.setMeta(new MetaModel(menu.getName(), menu.getIcon(), menu.getPath()));
                childrenList.add(children);
                router.setChildren(childrenList);
            }
            routers.add(router);
        }
        return routers;
    }

    /**
     * 构建前端所需要下拉树结构
     *
     * @param menus 菜单列表
     * @return 下拉树结构列表
     */
    @Override
    public List<Tree<String>> buildMenuTreeSelect(List<MenuBean> menus) {
        if (CollUtil.isEmpty(menus)) {
            return CollUtil.newArrayList();
        }
        return TreeBuildUtils.build(menus, (menu, tree) ->
                tree.setId(menu.getId())
                        .setParentId(menu.getPid())
                        .setName(menu.getName())
                        .setWeight(menu.getSort()));
    }

    /**
     * 是否存在菜单子节点
     *
     * @param menuId 菜单ID
     * @return 结果
     */
    @Override
    public boolean hasChildByMenuId(String menuId) {
        return baseMapper.exists(new LambdaQueryWrapper<SysMenu>().eq(SysMenu::getPid, menuId));
    }

    /**
     * 查询菜单使用数量
     *
     * @param menuId 菜单ID
     * @return 结果
     */
    @Override
    public boolean checkMenuExistRole(String menuId) {
        return roleMenuMapper.exists(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getMenuId, menuId));
    }

    /**
     * 校验菜单名称是否唯一
     *
     * @param menu 菜单信息
     * @return 结果
     */
    @Override
    public boolean checkMenuNameUnique(MenuBean menu) {
        boolean exist = baseMapper.exists(new LambdaQueryWrapper<SysMenu>()
                .eq(SysMenu::getName, menu.getName())
                .eq(SysMenu::getPid, menu.getPid())
                .ne(ObjectUtil.isNotNull(menu.getId()), SysMenu::getId, menu.getId()));
        return !exist;
    }

    /**
     * 根据父节点的ID获取所有子节点
     *
     * @param list     分类表
     * @param parentId 传入的父节点ID
     * @return String
     */
    private List<SysMenu> getChildPerms(List<SysMenu> list, String parentId) {
        List<SysMenu> returnList = new ArrayList<>();
        for (SysMenu t : list) {
            // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点
            if (t.getPid().equalsIgnoreCase(parentId)) {
                recursionFn(list, t);
                returnList.add(t);
            }
        }
        return returnList;
    }

    /**
     * 递归列表
     */
    private void recursionFn(List<SysMenu> list, SysMenu t) {
        // 得到子节点列表
        List<SysMenu> childList = StreamUtils.filter(list, n -> n.getPid().equals(t.getId()));
//		t.setChildren(childList);
        for (SysMenu tChild : childList) {
            // 判断是否有子节点
            if (list.stream().anyMatch(n -> n.getPid().equals(tChild.getId()))) {
                recursionFn(list, tChild);
            }
        }
    }

    /**
     * 删除菜单管理信息
     *
     * @param id 菜单ID
     * @return 结果
     */
    @Override
    public boolean delete(String id) {
        if (this.hasChildByMenuId(id)) {
            throw new ServiceException("存在子菜单,不允许删除");
        }
        if (this.checkMenuExistRole(id)) {
            throw new ServiceException("菜单已分配,不允许删除");
        }
        return SqlHelper.retBool(baseMapper.deleteById(id));
    }

}
